#ifndef _POINT_
#define _POINT_

#include <iostream>
#include <vector>
#include <algorithm>
#include <math.h>


using namespace std;

void abort(int error_code)
{
    switch (error_code)
    {
        case 1:
	    cout << "Dimension is not matched!" << endl;break;
        case 2:
	    cout << "Out of index!" << endl;break;
        case 3:
        cout << "Boundary error!" << endl;break;
        default:
	    break;
    }
    exit(-1);
};
/**
 *@brief Point<DIM>点类
 *
 */
template<unsigned int DIM>
class Point
{
protected:
    double value[DIM];/*<存储各个维度的坐标 */
public:
    /**
     *@brief 无参构造函数
     *
     */
    Point();
    /**
     * @brief 左值引用含参构造函数。
     *
     * @param Point_vector:左值引用，包含坐标。
     */
    Point(const vector<double>& _point_vector);
    /**
     * @brief 右值引用含参构造函数。
     *
     * @param Point_vector:右值引用，包含坐标。
     */
    Point(vector<double>&& _point_vector);
    /**
     * @brief 初始化列表含参构造函数。
     *
     * @param Point_list:初始化列表。
     */
    Point(const std::initializer_list<double>& _point_list);

    /**
     * @brief 默认析构函数
     *
     */
    ~Point() = default;
    /**
     * @brief: 重载[]操作符
     *
     * @param d 第d个维度
     *
     * @return 第d个维度的坐标
     */
    double& operator[](const unsigned int d);
    const double& operator[](const unsigned int d) const;
    Point<2>& operator = (const Point<2> _p);
    const Point<2> operator + (const Point<2> _p) const;
    const Point<2> operator - () const;
    const Point<2> operator - (const Point<2> _p) const;
    const Point<2> operator * (const double x) const;
    const Point<2> operator / (const double x) const;
    /**
     * @brief 友元函数，输出Point
     *
     * @param os:ostream
     * @param p:Point<DIM>
     *
     * @return
     */
    template<unsigned int D>
    friend ostream& operator<<(ostream& os, const Point<D> & p);
    /**
     * @brief 友元函数，计算两点间距离。
     *
     * @param p1:Point<DIM>
     * @param p2:Point<DIM>
     *
     * @return
     */
    template<unsigned int D>
    friend double distance(const Point<DIM> p1, const Point<D> p2);
};

template<unsigned int DIM>
Point<DIM>::Point()
{
    fill(value, value + DIM, static_cast<double>(0));
};

template<unsigned int DIM>
Point<DIM>::Point(const vector<double>& p_vector)
{
    if(p_vector.size() != DIM)
    {
	    abort(1);
    }
    vector<double>::const_iterator i_begin = p_vector.begin();
    vector<double>::const_iterator i_end = p_vector.end();
    copy(i_begin, i_end, value);
};

template<unsigned int DIM>
Point<DIM>::Point(vector<double>&& p_vector)
{
    if(p_vector.size() != DIM)
    {
	    abort(1);
    }
    vector<double>::const_iterator i_begin = p_vector.begin();
    vector<double>::const_iterator i_end = p_vector.end();
    copy(i_begin, i_end, value);//还是复制了，考虑把数组改成vector
    p_vector.clear();
};

template<unsigned int DIM>
Point<DIM>::Point(const std::initializer_list<double>& Point_list)
{
    if(Point_list.size() != DIM)
    {
        abort(1);
    }
    std::initializer_list<double>::const_iterator i_begin = Point_list.begin();
    std::initializer_list<double>::const_iterator i_end = Point_list.end();
    std::copy(i_begin, i_end,value);
};

template<unsigned int DIM>
double& Point<DIM>::operator[](const unsigned int d)
{
    return value[d];
}
template<unsigned int DIM>
const double& Point<DIM>::operator[](const unsigned int d) const
{
    return value[d];
}

template<unsigned int DIM>
Point<2>& Point<DIM>::operator = (const Point<2> _p)
{
    for(int d = 0; d < DIM; d++)
        this->value[d] = _p[d];
    return *this;
}

template<unsigned int DIM>
const Point<2> Point<DIM>::operator + (const Point<2> _p) const
{
    Point<DIM> p;
    for(int d = 0; d < DIM; d++)
    {
        p[d] = value[d] + _p[d];
    }
    return p;
}

template<unsigned int DIM>
const Point<2> Point<DIM>::operator - () const
{
    Point<DIM> p;
    for(int d = 0; d < DIM; d++)
    {
        p[d] = -value[d];
    }
    return p;
}

template<unsigned int DIM>
const Point<2> Point<DIM>::operator - (const Point<2> _p) const
{
    Point<DIM> p;
    p = *this + - _p;//同一段代码不重复写；如果-运算输出不加const，则此处输入const后无法继续运算（const point和point类不同）
    return p;
}

template<unsigned int DIM>
const Point<2> Point<DIM>::operator * (const double x) const
{
    Point<DIM> p;
    for(int i = 0; i < DIM; i++)
    {
        p[i] = value[i] * x;
    }
    return p;
}

template<unsigned int DIM>
const Point<2> Point<DIM>::operator / (const double x) const
{
    Point<DIM> p;
    p = *this * (1 / x);
    return p;
}

template<unsigned int D>
ostream& operator<<(ostream& os, const Point<D> & p)
{
    os << "(";
    for(unsigned int d = 0; d < D - 1; d++)
    {
        os << p.value[d] << ", ";
    }
    os << p.value[D - 1] << ")";
    return os;
}

template<unsigned int D>
double distance(const Point<D> p1, const Point<D> p2)
{
    double temp;
    for(unsigned int d = 0; d < D; d++)
        temp += (p1.value[d] - p2.value[d]) * (p1.value[d] - p2.value[d]);
    return sqrt(temp);
}

#else 
// DO NOTHING.
#endif
